package com.litong.litongjiaoyu.server;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.litong.litongjiaoyu.annotation.IgnoreAuth;
import com.litong.litongjiaoyu.entity.SysChatrecord;
import com.litong.litongjiaoyu.entity.SysWhochatwithwhom;
import com.litong.litongjiaoyu.service.SysChatrecordService;
import com.litong.litongjiaoyu.service.impl.SysWhochatwithwhomServiceImpl;
import com.litong.litongjiaoyu.utils.IdSequenceUtils;
import com.litong.litongjiaoyu.utils.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @ClassName WebSocket
 * @Description WebSocket服务类
 * @Author jingz
 * @Date 2020/1/24 19:05
 * @Version 1.0
 **/

@Component
@ServerEndpoint("/websocket/{username}")
public class WebSocket {
    private final Logger logger = LoggerFactory.getLogger(WebSocket.class);
    
    /**
     * 在线人数
     */
    
    public static int onlineNumber = 0;
    
    /**
     * 以用户的姓名为key，WebSocket为对象保存起来
     */
    private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
    /**
     * 会话
     */
    private Session session;
    /**
     * 用户名称
     */
    private String username;
    
    
    private static ApplicationContext applicationContext;
    
    /**
     * Redis操作
     */
    private RedisUtil redisUtil;
    
    /**
     * 聊天记录服务对象
     */
    private SysChatrecordService sysChatrecordService;
    
    /**
     * 引入雪花算法工具类
     */
    private IdSequenceUtils idSequenceUtils;
    
    /**
     * 谁与谁聊天记录服务对象
     */
    private SysWhochatwithwhomServiceImpl sysWhochatwithwhomServiceImpl;
    
    private SysWhochatwithwhom sysWhochatwithwhom;
    
    private SysChatrecord sysChatrecord;
    
    public static void SetApplicationConfig(ApplicationContext applicationContext) {
        WebSocket.applicationContext = applicationContext;
    }
    
    
    /**
     * 建立连接
     *
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) {
        
        onlineNumber++;
        logger.info("现在来连接的客户id：" + session.getId() + "用户名：" + username);
        this.username = username;
        this.session = session;
        logger.info("有新连接加入！ 当前在线人数" + onlineNumber);
        try {
            //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
            //先给所有人发送通知，说我上线了
            // Map<String, Object> map1 = new HashMap<String, Object>();
            // map1.put("messageType", 1);
            // map1.put("username", username);
            // sendMessageAll(JSON.toJSONString(map1), username);
            
            //把自己的信息加入到map当中去
            clients.put(username, this);
            
            //给自己发一条消息：告诉自己现在都有谁在线
            Map<String, Object> map2 = new HashMap<String, Object>();
            map2.put("messageType", 3);
            
            //移除掉自己
            Set<String> set = clients.keySet();
            map2.put("onlineUsers", set);
            sendMessageTo(JSON.toJSONString(map2), username);
    
            //引入redisUtil
            redisUtil = applicationContext.getBean(RedisUtil.class);
    
            sysChatrecordService = (SysChatrecordService) applicationContext.getBean("sysChatrecordService");
    
            //引入谁与谁聊天记录服务对象
            sysWhochatwithwhomServiceImpl = applicationContext.getBean(SysWhochatwithwhomServiceImpl.class);
            
            //获取是否存在该人的聊天记录
            List<Object> chats = redisUtil.lGet(username, 0, redisUtil.lGetListSize(username) - 1);
            
            //有，获取存在的聊天记录
            if (chats != null) {
                //删除之前存在的
                redisUtil.delete(username);
                
                //逐一发送
                for (Object chat : chats) {
                    sendMessageTo(JSON.toJSONString(chat), username);
                }
            }
            
        } catch (IOException e) {
            logger.error(username + "上线的时候通知所有人发生了错误");
        }
    }
    
    /**
     * 服务器端发生错误
     *
     * @param session
     * @param error
     */
    @IgnoreAuth
    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("服务端发生了错误" + error.getMessage());
    }
    
    /**
     * 连接关闭
     */
    @OnClose
    public void onClose() {
        onlineNumber--;
        //webSockets.remove(this);
        clients.remove(username);
        try {
            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息
            Map<String, Object> map1 = new HashMap<String, Object>();
            map1.put("messageType", 2);
            map1.put("onlineUsers", clients.keySet());
            map1.put("username", username);
            sendMessageAll(JSON.toJSONString(map1), username);
        } catch (IOException e) {
            logger.error(username + "下线的时候通知所有人发生了错误");
        }
        logger.info("有连接关闭！ 当前在线人数" + onlineNumber);
    }
    
    /**
     * 收到客户端的消息
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session) {
    
        idSequenceUtils = applicationContext.getBean(IdSequenceUtils.class);
        
        
        String textMessage = null;
        String fromusername = null;
        String tousername = null;
        String touserPhoto = null;
        String userPhoto = null;
        String openid = null;
        String type = null;
        
        int messageType = 0;
        
        try {
            logger.info("来自客户端消息：" + message + "客户端的id是：" + session.getId());
            JSONObject jsonObject = JSON.parseObject(message);
            
            //消息类型
            messageType = jsonObject.getInteger("messageType");
            //消息内容
            textMessage = jsonObject.getString("message");
            //发送人
            fromusername = jsonObject.getString("username");
            //接收人
            tousername = jsonObject.getString("to");
            //接收人头像
            touserPhoto = jsonObject.getString("touserPhoto");
            //发送人头像
            userPhoto = jsonObject.getString("userPhoto");
            
            //openid
            openid = jsonObject.getString("openid");
            
            //发送人类型，教师或者是学生
            type = jsonObject.getString("type");
            
            //如果不是发给所有，那么就发给某一个人
            
            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息 6表示图片信息
            Map<String, Object> map1 = new HashMap<String, Object>();
            map1.put("messageType", messageType);
            
            map1.put("textMessage", textMessage);
            map1.put("fromusername", fromusername);
            map1.put("touserPhoto", touserPhoto);
            map1.put("tousername", tousername);
            map1.put("fromuserPhoto", userPhoto);
            map1.put("openid", openid);
            
            //获取发送日期
            SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            simpleDateFormat1.setTimeZone(TimeZone.getTimeZone("GMT+08:00"));
            map1.put("sendtime", simpleDateFormat1.format(System.currentTimeMillis()));
            
            simpleDateFormat1.applyPattern("HH:mm");
            map1.put("time", simpleDateFormat1.format(System.currentTimeMillis()));
            
            /*
             修改时间：2020年6月2日09:04:07
             之前的方案不好用，容易发生错误，还影响系统性能
             新版方案：
             聊天记录保存在数据库中，分为两个表:
             一个是谁和谁聊天记录表，字段：ID， name （形式，按照字典排序：景泽培冯浩星）
             另一个是具体聊天内容，字段：id, 聊天记录表id，发送人， 接收人， 消息类型， 消息内容，发送时间， 发送人微信头像
             
             
             
             聊天信息保存到数据库
             将接受者和发送者的名字按照升序排序，然后转化成字符串，查询数据库中是否有此记录，有则返回id
             没有则返回空
             */
            String[] names = {fromusername, tousername};
            String name = this.join(names);
            
            String whoAndWhoID = null;
            
            sysChatrecord = new SysChatrecord();
            
            //设置发送人
            sysChatrecord.setSender(fromusername);
            //设置接收人
            sysChatrecord.setReceiver(tousername);
            //设置发送时间
            sysChatrecord.setSendtime(
                    new Date(
                            System.currentTimeMillis()
                    )
            );
            //设置消息类型
            sysChatrecord.setMessagetype(messageType);
            //设置消息内容
            sysChatrecord.setMessage(textMessage);
            //设置发送者头像
            sysChatrecord.setAvatar(userPhoto);
            
            //判断是否存在这两人聊天记录，如果存在直接保存，如果不存在，调用雪花算法，然后在保存
            if ((whoAndWhoID = this.sysWhochatwithwhomServiceImpl.presence(name)) != null) {
                //设置ID
                sysChatrecord.setWhoandwhoid(whoAndWhoID);

            } else {
                //设置ID
                whoAndWhoID = Long.toString(idSequenceUtils.nextId());
                //放入对象
                sysChatrecord.setWhoandwhoid(whoAndWhoID);

                sysWhochatwithwhom = new SysWhochatwithwhom();

                sysWhochatwithwhom.setId(whoAndWhoID);

                //设置谁与谁聊天，升序
                sysWhochatwithwhom.setWhoandwho(
                        this.join(
                                new String[]{fromusername, tousername}
                        )
                );
                
                if (!this.sysWhochatwithwhomServiceImpl.insert(sysWhochatwithwhom)) {
                    throw new RuntimeException("双方聊天记录保存至数据库失败");
                }
            }

            //保存详细聊天记录
            this.save();


            /*
               发送信息
             */
            if (tousername.equals("All")) {
                map1.put("tousername", "所有人");
                
                sendMessageAll(JSON.toJSONString(map1), fromusername);
            } else {
                
                map1.put("tousername", tousername);
                
                sendMessageTo(JSON.toJSONString(map1), tousername);
                //判断用户是否在线
                if (!clients.containsKey(tousername)) {
                    //用户不在线
                    //聊天记录保存在redis
                    redisUtil.lSet(tousername, map1);
                    
                    StringBuilder stringBuilder = new StringBuilder();
                    stringBuilder.append(tousername);
                    stringBuilder.append("Notice");
                    logger.error(stringBuilder.toString());
                    if (! redisUtil.hasKey(stringBuilder.toString())) {
                        //发送微信通知,暂时有问题，无法发出信息
                        sysChatrecordService.sendWxMessage(openid, fromusername, type);
                    }
                }
            }
        } catch (Exception e) {
            logger.error(
                    String.format("%s -> %s 发生错误：%s",
                            e.getStackTrace()[0].getClassName(),
                            e.getStackTrace()[0].getMethodName(),
                            e.getMessage())
            );
            
            e.printStackTrace();
            
            Map<String, Object> map1 = new HashMap<String, Object>(3);
            map1.put("messageType", 5);
            
            map1.put("textMessage", "消息发送出现失败，请稍后重试或提供截图，手机号进行反馈");
            map1.put("fromusername", fromusername);
            
            try {
                sendMessageTo(JSON.toJSONString(map1), fromusername);
            } catch (Exception a) {
                logger.error("发生严重错误，无法传递错误信息");
            }
        }
    }
    
    
    public void sendMessageTo(String message, String ToUserName) throws IOException {
        
        for (WebSocket item : clients.values()) {
            if (item.username.equals(ToUserName)) {
                
                item.session.getAsyncRemote().sendText(message);
                /**
                 *  getBasicRemote()  阻塞式 同步
                 *  getAsyncRemote() 非阻塞式 异步
                 */
                
                break;
                
            }
        }
        
    }
    
    public void sendMessageAll(String message, String FromUserName) throws IOException {
        
        for (WebSocket item : clients.values()) {
            item.session.getAsyncRemote().sendText(message);
        }
        
    }
    
    /**
     * 获取在线人数
     *
     * @return 在线人数
     */
    public static synchronized int getOnlineCount() {
        
        return onlineNumber;
        
    }
    
    /**
     * 保存到数据库
     */
    public synchronized boolean save() throws RuntimeException {
        if (this.sysChatrecordService.insert(sysChatrecord)) {
            return true;
        } else {
            throw new RuntimeException("保存详细聊天记录失败");
        }
    }
    
    /**
     * 保存到redis中
     *
     * @param map 要发送的消息内容
     *
     * @return 是否保存成功
     * @throws RuntimeException 保存失败
     */
    public boolean saveToRedis(Map<String, Object> map) throws RuntimeException {
        
        //在redis查找是否存在此人的聊天记录
        return redisUtil.lSet(map.get("tousername").toString(), map);
    }
    
    /**
     * 将数组转化成字符串并返回
     * @param names 发送者和接收者姓名
     * @return 升序排序后的字符串
     */
    public String join(String[] names) {
        //升序排序
        Arrays.sort(names);
    
        //转化成字符串
        return StringUtils.join(names, ",");
    }
    
    /**
     * 将字符串转化为字符串数组，并按照字典的顺序排序
     * @param name 要整理的内容
     * @return 整理后的字符串数组
     */
    public String[] format(String name){
        //去除字符串内大部分空白字符， 不限于空格
        name = name.replace("\\s*", "");
    
        //根据 “ , ” 分割字符串
        String[] names = name.split(",");
    
        Arrays.sort(names);
    
        return names;
    }
    
    
    
}
